import struct, ossaudiodev, math, threading, time

"""Generate waveforms"""

WAVE_SINE = 1
WAVE_SQUARE = 2
WAVE_RAMP = 3
WAVE_NOISE = 4
WAVE_ARBITRARY = 5

"""
Design note:	We are re-generating every block because they are not all the same (different points in the wave).
		This is rather inefficeint. We could instead generate one block that contains one or more complete waveforms / cycles.
		This can then be stored and re-sent to the buffer as required.
		Wave can be re-generated only when settings are changed. i.e. move to setWaveform()

"""

class WaveGeneratorThread(threading.Thread):
	"""Continuously outputs waveform to sound card device"""
	def __init__(self, device = '/dev/dsp1', sampleRate = 44100, blockSize = 4410, minBufferBlocks = 2):
		"""Initialise"""
		threading.Thread.__init__(self)
		
		self.sampleRate = sampleRate
		self.blockSize = blockSize
		self.minBufferBlocks = minBufferBlocks
		
		# Set default wave parameters
		self.setWaveform()
		# Wave time position (used to keep continuity in wave phase between blocks)
		self.wavePos = 0
		
		# Open audio deivice
		self.dsp = ossaudiodev.open(device,'w')
		# Set for 16 bit 1 channel
		self.dsp.setparameters(ossaudiodev.AFMT_S16_NE, 1, self.sampleRate)
		
		# Generator runs while true
		self.running = True
		
		self.outputEnabled = False
		
		
	def run(self):
		"""Main loop for thread"""
		
		while self.running:
		
			# If the number of blocks left in the buffer is less than specified minimum then add another block.
			if self.dsp.obufcount() < (self.blockSize * self.minBufferBlocks) and self.outputEnabled:

				# Genterate waveform block				
				sig = self.generateWaveBlock()
				
				##take the upper and lower boundary of the signal 
				min_sig = min(sig)
				max_sig = max(sig)
				
				# Note : this dynamic scaling needs to come out, as it invalidates the user set amplitude.
				
				##Finding the scaling factor of the signal to the 16bit resolution
				ambit = max(max_sig, -min_sig)
				scale = ( 2**16 - 1 )/( 2.0*ambit )
	
				##Scaling the signal for 16 bit
				sig_16bit = [ int(scale*x) for x in sig ]
	
				data = '' ## sound will now be a string.

				for i in range( len(sig_16bit) ):
					data += struct.pack('h',sig_16bit[i]) ## make the sound object

				self.dsp.write(data)
			
			else:
				# Wait 1ms before checking buffer size
				time.sleep(0.001)
		
		# Thread has been told to terminate
		# Close audio device
		self.dsp.close()
	
	
	def terminate(self):
		"""Terminate the thread"""
		self.running = False
	
	
	def generateWaveBlock(self):
		"""Generate another block of the wave"""
		
		sig = []
		# Create a block continuing from last wave position
		for x in range(self.wavePos, self.wavePos + self.blockSize):
		
			if self.wave == WAVE_SINE:
				sig.append( math.sin( 2.0 * math.pi * self.frequency * x / self.sampleRate ) )
				
			elif self.wave == WAVE_SQUARE:
				# Temp lazy method
				if math.sin( 2.0 * math.pi * self.frequency * x / self.sampleRate ) > 0:
					sig.append(2)
				else:
					sig.append(-2)
			
			elif self.wave == WAVE_RAMP:
				pass
			
			elif self.wave == WAVE_NOISE:
				pass
			
			elif self.wave == WAVE_ARBITRARY:
				pass
			
		self.wavePos += self.blockSize
		return sig
	
	
	def setWaveform(self, wave = WAVE_SINE, frequency = 100, amplitude = 1, phase = 0, sweep = None, duty = None, offset = 0):
		"""Configure the waveform to be generated"""
		self.wave = wave
		self.frequency = frequency





